<?php
// Copyright 2019 Hackware SpA <human@hackware.cl>
// This file is part of "Hackware Web Services Wallet" and licensed under
// the terms of the GNU Affero General Public License version 3, or (at your
// option) a later version. You should have received a copy of this license
// along with the software. If not, see <https://www.gnu.org/licenses/>.

namespace Hawese\Wallet;

use Hawese\Core\User;
use Hawese\Core\Exceptions\ModelObjectNotFoundException;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Carbon;

class Wallet extends TableModel
{
    public static $table = 'wallets';
    public static $attributes = [
        'user_uid' => ['required', 'string', 'min:3', 'max:100'],
        'currency_code' => [
            'required', 'string', 'size:3', 'exists:currencies,code'
        ],
        'created_at' => ['nullable', 'date'],
        'updated_at' => ['nullable', 'date'],
        'deleted_at' => ['nullable', 'date']
    ];

    public static $primary_key = 'user_uid';
    protected static $incrementing = false;

    public function __construct($data)
    {
        // Virtual properties
        $this->append('due_at');
        $this->append('balance');
        parent::__construct($data);
    }

    public function setDueAt($value) : void
    {
        $this->dateSetter('due_at', $value);
    }

    public function getDueAt()
    {
        if (!array_key_exists('due_at', $this->data)) {
            $query = Transaction::select(['due_at'])
                ->where('user_uid', '=', $this->user_uid)
                ->whereNotNull('due_at')
                ->orderBy('due_at', 'asc');

            $this->due_at = $query->exists() ? $query->first()->due_at : null;
        }
        return $this->data['due_at'];
    }

    public function getBalance()
    {
        if (!array_key_exists('balance', $this->data)) {
            $this->balance = Transaction::select(['balance'])
                ->latest()->first()->balance;
        }
        return $this->data['balance'];
    }

    /*
    // I will leave it here just in case, probably will delete it in future.
    // It preprocesses due_at and balance values, but seems too hacky for me.
    public static function select(?array $attributes = null) : Builder
    {
        return app('db')->table(static::$table)
            ->select(array_merge(
                array_map(
                    function ($attribute) {
                        return self::$table . '.' . $attribute;
                    },
                    static::attributes()
                ),
                [app('db')->raw('MIN(due_at) as due_at,' .
                    '(SELECT balance' .
                    ' FROM ' . Transaction::$table . ' WHERE ' .
                    Transaction::$table.'.user_uid = '.self::$table.'.user_uid'
                    . ' ORDER BY id DESC LIMIT 1) as balance')]
            ))
            ->leftJoin(
                Transaction::$table,
                Transaction::$table . '.user_uid',
                '=',
                self::$table . '.user_uid'
            )
            ->groupBy(
                'wallets.user_uid',
                'currency_code',
                'wallets.created_at',
                'wallets.updated_at',
                'wallets.deleted_at'
            );
    }
    */
    
    public function insert(): bool
    {
        // FIXME: Validated twice! Check hawese-core issue #5
        $this->validate();

        try {
            $user = User::find($this->user_uid);
        } catch (ModelObjectNotFoundException $e) {
            $user = new User(['uid' => $this->user_uid]);
            if (strpos($this->user_uid, '@') !== false) {
                $user->email = $this->user_uid; // TODO: Sure about this?
            }
            $user->insert();
        }
        return parent::insert();
    }

    public function initialTransaction() : bool
    {
        $transaction = new Transaction([
            'user_uid' => $this->user_uid,
            'currency_id' => Currency::findByCode($this->currency_code)->id,
            'amount' => '0',
            'type' => 'system',
            'description' => 'Wallet creation'
        ]);

        return $transaction->insert();
    }

    public function isOwner(User $user): bool
    {
        return $this->user_uid === $user->uid;
    }
}
